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:443FeeBps 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 path | gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/auction |
|---|---|
| Namespace | g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3 |
| Deployed at height | 488,838 |
| Deploy transaction | 9fe0d06bab5b0b52c98c08e7420b5db8292fbbd3cbe8ad28ce1dcb2b0d3adc33 look it up on the RPC |
| Deployer | g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3 |
| Gas used | 31,714,307 |
| Storage | not attested per package on this chain — see the limitations below |
| Files on chain | auction.gno gnomod.toml |
| Deployed bytes | auction.gno — 22,850 bytes |
| sha256 | dbefcaf6e787a25e3402fdad217fab45f9c399a4df598e764b0b2e8deb43a26c |
| Realm address | g10j5374ns0sayj9d4put6c8nlz4vzm0q86vyk44 |
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 256Expected: 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
chainchain/runtimechain/runtime/unsafegno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/coiniogno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/feeledgergno.land/p/nt/avl/v0gno.land/p/nt/markdown/sanitize/v0strconv
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 asAcceptAdmin(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 asBid(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 asCancel(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 asClaim(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 asClaimAll(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 asClose(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 asCreateAuction(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 asSetFeeBps(cross(cur), ...).
SetFeeRecipient
func SetFeeRecipient(cur realm, recipient address)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asSetFeeRecipient(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 asSweepDenom(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 asTransferAdmin(cross(cur), ...).
WithdrawFees
func WithdrawFees(cur realm)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asWithdrawFees(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| Imports | chain, 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 dependencies | gno.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- PAYMENT-ONLY; DELIVERY IS OFF-CHAIN. The realm settles the winner's payment and records the winner; it does not custody or deliver the lot (free text). An on-chain-asset auction (grc20/grc721 custody) is a v2.
- THE SELLER IS THE CREATOR. Only bidders are EOA-enforced (coinio's guard). A realm seller may create an auction but must expose its own Claim path or its proceeds strand -- the seller's concern, the revshare caveat, not a realm or third-party hazard.
- FIXED 5% minimum increment and a FLAT protocol fee -- not a per-auction increment knob and not a tiered-by-value fee (p/tiers could price one). A v2.
- AN AUCTION CAN RUN INDEFINITELY under sustained anti-snipe bidding -- that is the anti-snipe design (the auction ends when bidding stops), not a defect; antiSnipe=0 disables it.
- TERMINAL AUCTIONS KEEP THEIR PER-SELLER SLOT (a settled/closed/cancelled auction is a permanent record), bounded by MaxAuctionsPerSeller=50 (monotone, no decrement by design).
- THE LIVE FEE WAS 0 bps: auction 0 carried the deploy-time default feeBps=0, so the settle credited the seller the gross. The 250-bps split is proven by the committed suite (TestCloseWithWinnerSettlesSeller), not live. Anti-snipe, the reserve floor, the min-increment refusal, Cancel, seller-cannot-bid and a no-bids Close are likewise suite-only (auction 0 used reserve 0, antiSnipe 0, and a single raise).
- storage_bytes and storage_deposit are null for the AL56 reason in storage_measurement_note; the figures ARE chain-attested (53,434 B / 5,343,400ugnot at 488842). Do not read the nulls as an unmeasured deploy.
Source
| Source file | onyx/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 bytes | yes — byte-identical |