Cosmic Bull

auction

gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/auction on onyx-1 testnet

realmapplicationauctionslive on onyx-1

Sell something to the highest bidder, with the coins held in escrow.

Outbid bidders can always reclaim what they put in, and a late bid extends the clock so the sale closes when bidding actually stops.

In detail curated

ASCENDING (ENGLISH) AUCTION settled in ugnot -- the one market shape the portfolio never had: COMPETITIVE PRICE DISCOVERY with escrowed settlement. A seller lists a lot with a reserve and a duration; bidders raise each other by ATTACHING coins; the realm escrows the standing high bid and every OUTBID bid becomes a pull-claimable refund (fee 0 -- a full return, not a fee event); after the end height anyone may Close, crediting the winner's payment to the seller with the protocol fee split; a bid inside the anti-snipe window extends the end. It is none of the shipped shapes: routing (fee_split/revshare) moves a known amount to known parties; disbursement (vesting/payroll/budgets) pays a known total out on a schedule; credit (loans) lends principal out for a larger total back; crowdfund pools many contributions toward one shared goal. CUSTODY -- a custodial realm, so the monetary invariant governs it as it does loans/subscriptions, with one extra bucket: Held == locked + feeledger.Liabilities(), where locked is the sum of the current high bids of open auctions. Refunds are PULL, never push, so a bid cannot be griefed by a hostile previous bidder and there is no reentrancy surface in the bid path. SCOPE: the realm settles PAYMENT and records the WINNER; it does NOT custody or deliver the lot (free text, off-chain delivery) -- an on-chain-asset auction (grc20/grc721) is a v2. AUTHORIZATION: bidders via coinio's EOA guard; seller is the creator (a realm seller must expose its own Claim path or strand, the revshare caveat); seller may not bid on their own auction; Close permissionless after end; Cancel seller-only and pre-bid-only; admin two-step. Audit led with fund safety: PROCEED, 0 RED / 0 material YELLOW, four candidates all resolved to GREEN/removed; the one GREEN hygiene note (anti-snipe checked-add symmetry) was fixed and every figure re-measured. 24-case mutation sweep FULLY COVERED; 18-test suite green.

Use it

A realm holds state. Reading it is a free query; changing it is a transaction you sign yourself.

Read it — no key, no account, no GNOT

gnokey query vm/qeval \
  --data 'gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/auction.FeeBps()' \
  --remote https://rpc.onyx.testnets.gno.land:443

FeeBps is read-only, so this is a query rather than a transaction: it costs nothing, signs nothing and needs no funded account. Every exported function is listed under the API below; swap the name and the arguments to read any of the others.

Change its state

Every state change is a transaction you sign with your own key — this site never asks for one and holds none. You do not have to hand-write the transaction: gnoweb generates the exact gnokey maketx call command for any of this realm's 12 crossing functions, with the argument names filled in.

Read the known limitations further down this page first. They record what was not verified on chain, which is usually the part that matters.

Import it from another realm

import "gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/auction"

The path is the address: there is no package manager, nothing to add to a manifest and no version to pin, so the package you compile against is the one the chain runs. It differs per chain because the namespace in it is the deploying account's.

Deployments

onyx-1 testnet onyx-1

RenderedSourceCall builderState

chain-attested
Import pathgno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/auction
Namespaceg1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3
Deployed at height488,838
Deploy transaction9fe0d06bab5b0b52c98c08e7420b5db8292fbbd3cbe8ad28ce1dcb2b0d3adc33 look it up on the RPC
Deployerg1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3
Gas used31,714,307
Storagenot attested per package on this chain — see the limitations below
Files on chainauction.gno gnomod.toml
Deployed bytesauction.gno — 22,850 bytes
sha256dbefcaf6e787a25e3402fdad217fab45f9c399a4df598e764b0b2e8deb43a26c
Realm addressg10j5374ns0sayj9d4put6c8nlz4vzm0q86vyk44

Do not take the hash above on trust. $download returns the bytes onyx-1 is actually running; this command fetches them and prints their digest, which should equal the one in the table:

curl -sS 'https://onyx.testnets.gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/auction$download&file=auction.gno' | shasum -a 256

Expected: dbefcaf6e787a25e3402fdad217fab45f9c399a4df598e764b0b2e8deb43a26c — 22,850 bytes. Use curl: the gno.land edges answer Python's default user-agent with HTTP 403.

API

chain-derived 33 exported functions.

Every function below deep-links to gnoweb's call builder, which generates a ready-to-run gnokey maketx call for it:

AcceptAdminAuctionInfoAuctionsOfBidBlocksLeftCancelClaimClaimAllClaimableOfCloseCreateAuctionEndHeightFeeBpsFeesAccruedHasAuctionHeightHeldHighBidIsCloseableIsOpenLiabilitiesLockedLotMinNextBidNumAuctionsRenderReserveSetFeeBpsSetFeeRecipientStatusSweepDenomTransferAdminWithdrawFees

Overview

Realm auction is an ascending (English) auction settled in ugnot, holding real coins. A SELLER lists a lot with a reserve price and a duration; BIDDERS raise each other by attaching coins; the standing high bid is escrowed by the realm and every OUTBID bid becomes a pull-claimable refund; at close the winner's payment is credited to the seller, the protocol fee splitting off at the ledger. A bid landing inside the anti-snipe window extends the end, so the auction closes only when bidding actually stops.

It is the one market shape this portfolio never had: COMPETITIVE PRICE DISCOVERY with escrowed settlement. The routing realms move a known amount to known parties (r/fee_split, r/revshare), the disbursement realms pay a known total out on a schedule (r/vesting, r/payroll, r/budgets), the credit realm lends principal out and takes a larger total back (r/loans), and r/crowdfund POOLS many contributions toward one shared goal. An auction is none of those: N rival bidders, one escrowed winner, automatic refunds for the rest, and a price nobody knows in advance. See [DISCOVERY.md].

Custody — the conservation invariant is the product

A custodial realm, so the monetary invariant governs it exactly as it does r/loans and r/subscriptions, with one extra bucket for the escrowed bids:

Held == locked + feeledger.Liabilities()

where `locked` is the sum of the CURRENT high bids across all open auctions (coins the realm holds but that are not yet claimable by anyone), and the ledger holds the two claimable kinds — OUTBID refunds and, after close, SELLER proceeds — plus the fee pot. Every value transition preserves the equality; see the DELTA-form conservation test.

Refunds are PULL, never push: an outbid bid is credited to the bidder's claimable balance (fee 0 — a refund is a full return, not a fee event) and the bidder withdraws it with Claim. Nothing is sent to a third party during someone else's Bid, so a bid cannot be griefed by a hostile previous bidder and there is no cross-call reentrancy surface in the bid path.

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 identified by a free-text description and changes hands off-chain (or by a downstream realm reading [Winner]). This is the scope decision, stated rather than discovered: an on-chain-asset auction (grc20/grc721 custody) is a v2, and it is exactly the escrow-of-the-asset surface this v1 deliberately omits. A winner pays and is recorded; delivery is the parties' concern, as it is for r/loans' off-chain use of principal.

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 CreateAuction; a realm may create an auction, but like revshare's members a realm seller must expose its own crossing path to Claim or its proceeds strand — the seller's concern, not a realm or third-party hazard. assertNoSend reads the ORIGIN envelope, so every non-payable function refuses any transaction that attached coins anywhere.

Imports

Constants and variables

Auction status values. Settled, Closed and Cancelled are terminal.

const (
	AuctionOpen      = "open"      // accepting bids until endHeight
	AuctionSettled   = "settled"   // closed WITH a winner; proceeds credited
	AuctionClosed    = "closed"    // closed with NO bids; nothing to settle
	AuctionCancelled = "cancelled" // seller cancelled before any bid
)

Input bounds.

const (
	// MinBid is the dust floor for any bid and any non-zero reserve; MaxBid
	// keeps every bid, the increment and the fee split far from int64
	// overflow. The bidder must ATTACH the bid, so the real ceiling is their
	// balance.
	MinBid = int64(1_000)
	MaxBid = int64(1_000_000_000_000) // 1,000,000 GNOT

	// Duration and anti-snipe bounds, in blocks. The floor keeps an auction
	// off a one-block treadmill; the ceiling keeps height arithmetic far from
	// overflow. A 0 anti-snipe window disables the extension.
	MinDuration  = int64(10)
	MaxDuration  = int64(7_500_000)
	MaxAntiSnipe = int64(7_500_000)

	// MaxLotLen bounds the stored lot description.
	MaxLotLen = 200

	// MaxAuctionsPerSeller bounds one seller's stored auctions; terminal
	// auctions do NOT free the slot, because a settled/closed/cancelled
	// auction is a permanent record a bidder and auditor may need to read.
	MaxAuctionsPerSeller = 50

	// RenderLimit bounds every rendered list — an unbounded Render was YELLOW
	// in three prior audits.
	RenderLimit = 20
)

Denom is the only asset this realm accepts.

const Denom = "ugnot"

MaxFeeBps is the hard protocol-fee cap: 1000 bps = 10%. The fee applies only to the WINNER's payment at close, never to a refund.

const MaxFeeBps = int64(1000)

MinIncrementBps is the minimum raise over the standing high bid: 500 = 5%. It stops penny-increment griefing without a per-auction knob.

const MinIncrementBps = int64(500)

Functions

AcceptAdmin

func AcceptAdmin(cur realm)

Crossing function. Callable from a transaction via MsgCall, and from another realm as AcceptAdmin(cross(cur), ...).

AuctionInfo

func AuctionInfo(auctionID int64) (seller address, reserve, highBid, endHeight, antiSnipe, feeBps, createdHeight int64, highBidder address, status string)

AuctionInfo returns an auction's terms and standing state. Lot text is a separate view (Lot) so this stays a numeric/address tuple.

AuctionsOf

func AuctionsOf(seller address) int64

Bid

func Bid(cur realm, auctionID int64)

Crossing function. Callable from a transaction via MsgCall, and from another realm as Bid(cross(cur), ...).

Bid raises the standing high bid on an open auction. The caller attaches the bid; it must beat the current high bid by at least MinIncrementBps (or meet max(reserve, MinBid) if it is the first bid), and the auction must still be open and before its end. The previous high bidder's escrowed bid becomes a pull-claimable refund. A bid inside the anti-snipe window extends the end.

BlocksLeft

func BlocksLeft(auctionID int64) int64

BlocksLeft is how many blocks until the end (0 once ended).

Cancel

func Cancel(cur realm, auctionID int64)

Crossing function. Callable from a transaction via MsgCall, and from another realm as Cancel(cross(cur), ...).

Cancel withdraws an auction that has not yet received a bid. Seller only. Once any bid stands, the auction can only run to close — a seller cannot pull the lot out from under a committed bidder.

Claim

func Claim(cur realm, amount int64)

Crossing function. Callable from a transaction via MsgCall, and from another realm as Claim(cross(cur), ...).

Claim sends amount ugnot of the caller's claimable balance — outbid refunds and, if the caller is a seller of a settled auction, proceeds — to the caller.

ClaimAll

func ClaimAll(cur realm)

Crossing function. Callable from a transaction via MsgCall, and from another realm as ClaimAll(cross(cur), ...).

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

ClaimableOf

func ClaimableOf(a address) int64

Close

func Close(cur realm, auctionID int64)

Crossing function. Callable from a transaction via MsgCall, and from another realm as Close(cross(cur), ...).

Close finalizes an auction whose end height has passed. Anyone may call it; no auction's terminal state may depend on a party acting. With a winner, the high bid is credited to the seller (fee split at the ledger) and the auction is Settled; with no bids it is Closed. The winner's escrowed payment moves from `locked` into the seller's claimable balance.

CreateAuction

func CreateAuction(cur realm, lot string, reserve, durationBlocks, antiSnipe int64) int64

Crossing function. Callable from a transaction via MsgCall, and from another realm as CreateAuction(cross(cur), ...).

CreateAuction lists a lot for ascending-bid sale. The caller becomes the seller. `reserve` is 0 (no reserve) or a floor in [MinBid, MaxBid] the first bid must meet; the auction runs for `durationBlocks` blocks; a bid landing within `antiSnipe` blocks of the end extends the end to now+antiSnipe (0 disables). The current protocol fee is snapshotted in. Returns the id.

EndHeight

func EndHeight(auctionID int64) int64

EndHeight is the current end height (which anti-snipe may have extended).

FeeBps

func FeeBps() int64

FeesAccrued

func FeesAccrued() int64

HasAuction

func HasAuction(auctionID int64) bool

Height

func Height() int64

Held

func Held() int64

HighBid

func HighBid(auctionID int64) int64

HighBid is the standing high bid (0 if none).

IsCloseable

func IsCloseable(auctionID int64) bool

IsCloseable reports whether Close would succeed right now.

IsOpen

func IsOpen(auctionID int64) bool

IsOpen reports whether the auction is open and still before its end.

Liabilities

func Liabilities() int64

Locked

func Locked() int64

Lot

func Lot(auctionID int64) string

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

MinNextBid

func MinNextBid(auctionID int64) int64

MinNextBid is the smallest bid that would be accepted right now: the first bid's floor when there are no bids, else the standing high bid plus the minimum increment.

NumAuctions

func NumAuctions() int64

Render

func Render(path string) string

Reserve

func Reserve(auctionID int64) int64

Reserve is the auction's reserve price (0 if none).

SetFeeBps

func SetFeeBps(cur realm, bps int64)

Crossing function. Callable from a transaction via MsgCall, and from another realm as SetFeeBps(cross(cur), ...).

SetFeeRecipient

func SetFeeRecipient(cur realm, recipient address)

Crossing function. Callable from a transaction via MsgCall, and from another realm as SetFeeRecipient(cross(cur), ...).

Status

func Status(auctionID int64) string

Status is the auction's status string.

SweepDenom

func SweepDenom(cur realm, denom string)

Crossing function. Callable from a transaction via MsgCall, and from another realm as SweepDenom(cross(cur), ...).

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

TransferAdmin

func TransferAdmin(cur realm, successor address)

Crossing function. Callable from a transaction via MsgCall, and from another realm as TransferAdmin(cross(cur), ...).

WithdrawFees

func WithdrawFees(cur realm)

Crossing function. Callable from a transaction via MsgCall, and from another realm as WithdrawFees(cross(cur), ...).


Doc text is reproduced as vm/qdoc returns it. The node markdown-escapes doc comments, so a bracket or angle bracket may carry a backslash the committed source does not have. The source itself is at source and in this repository.

Dependencies

chain-attested
Importschain, chain/runtime, chain/runtime/unsafe, gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/coinio, gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/feeledger, gno.land/p/nt/avl/v0, gno.land/p/nt/markdown/sanitize/v0, strconv
First-party dependenciesgno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/coinio, gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/feeledger

Known limitations

Recorded by the people who built and deployed it. This list is deliberately not empty where honesty costs something.

curated

Source

Source fileonyx/r/auction/auction.gno at commit c2e10d17ffbf in the project repository (not public — the digest command above is the check that needs no repository)
Matches the deployed bytesyes — byte-identical