const StatusActive, StatusSold, StatusCancelled
Listing status values.
Realm market is a custodial GNOT marketplace for goods listings: sellers list (title, description, price), a buyer pu...
Realm market is a custodial GNOT marketplace for goods listings: sellers list (title, description, price), a buyer purchases by paying the exact price, the marketplace holds the value until the seller claims their proceeds (price minus a transparent, snapshotted protocol fee).
COMPOSITION (per the recorded DISCOVERY / REUSE ANALYSIS): balance accounting is feeledger, coin movement is coinio, free-text render safety is the ecosystem sanitizer p/nt/markdown/sanitize/v0. This realm owns only the listing state machine. Two patterns are adopted from inspected ecosystem marketplaces: non-payable entrypoints REFUSE accidental -send instead of stranding it as surplus (nsmarket/v4's assertNoSend), and admin handoff is 2-STEP (memba_appstore_v2's TransferOwnership/AcceptOwnership), closing the one-step-transfer trade-off carried by earlier realms.
LIFECYCLE (terminal states are frozen; one transition per listing):
1CreateListing (anyone, no coins) : status Active; the fee bps is
2 SNAPSHOTTED, subject to the
3 seller's own maxFeeBps ceiling
4Buy (EOA + -send == price) : Active -> Sold, atomically:
5 proceeds (price - snapshot fee)
6 credit the seller's claimable
7 balance, the fee accrues to the
8 pot, the buyer is recorded
9CancelListing (seller only) : Active -> Cancelled (no funds
10 are involved; listings hold no
11 value)
12Claim / ClaimAll (anyone) : pays out the caller's own
13 claimable proceeds
14WithdrawFees (fee recipient) : pays out the fee pot
LISTINGS ARE IMMUTABLE: there is no price update — cancel and relist (new id). Together with Buy's EXACT-envelope rule this closes the listing-manipulation race twice over: a cancelled/relisted listing fails Buy's status check, and any price change fails the envelope check — either way the buyer's coins revert with the transaction. Buyers are structurally indifferent to fee changes: they pay the listed price; the fee comes out of the seller's proceeds at the bps snapshotted when the SELLER listed (with the seller's own ceiling — the creation-time fee race is closed the same way grants closes it).
AUTHORIZATION: every identity derives from the crossing entrypoint's cur.Previous().Address(); no function takes a caller identity as a parameter. Sellers may be EOAs or realms (they claim under their own address); buyers must be EOAs (coinio.Receive is the receipt-guaranteed shape). Self-purchase is rejected.
REALM-SELLER CAVEAT (audit Y1): assertNoSend reads the ORIGIN transaction's send envelope, so a realm seller must call CreateListing/CancelListing/Claim* in a transaction whose origin carried no -send — otherwise the guard fails closed even though this realm received nothing. Not third-party triggerable (nobody can attach a send to someone else's transaction); the workaround is a separate transaction.
MONETARY INVARIANT (conservation): listings hold NO value, so with H = ugnot held at the realm address, U = claimable seller proceeds, F = the fee pot, S >= 0 out-of-band surplus:
1H == U + F + S
Buy raises H by exactly price and U+F by exactly price (feeledger guarantees credited + fee == amount); Claim*/WithdrawFees debit the ledger before coinio.Payout moves the identical amount out; any panic aborts the whole transaction; this realm never issues or removes coins. Surplus is recoverable only via SweepDenom (fee recipient), which reserves Liabilities() = U + F.
APPLICATION INVARIANT: status transitions Active -> {Sold, Cancelled} exactly once; Sold if and only if a buyer is recorded; for every sold listing, proceeds + fee == price at the snapshotted bps.
Listing status values.
Input bounds.
Denom is the only asset this realm accepts.
MaxFeeBps is the hard protocol-fee cap: 1000 bps = 10%.
AcceptAdmin completes a staged admin handoff. Only the staged address may call it.
BalanceOf returns addr's claimable proceeds.
Buy purchases an active listing. The buyer must be a direct EOA caller and attach EXACTLY the listed price in ugnot — any mismatch (including a price the seller changed by cancel-and-relist) aborts and the coins revert with the transaction. Settlement is atomic: the seller's proceeds (price minus the snapshotted fee) become claimable, the fee accrues to the pot, and the buyer is recorded. Terminal.
CancelListing withdraws an active listing. Only the seller may cancel; no funds are involved. Terminal.
Claim sends amount ugnot of the caller's claimable proceeds back to the caller.
ClaimAll sends the caller's entire claimable proceeds back to the caller. Fails if there is nothing to claim.
CreateListing publishes an immutable listing and returns its id. No coins are accepted (the storage deposit the caller pays is the anti-spam). The current protocol fee is snapshotted into the listing and must not exceed maxFeeBps, the ceiling the seller signed for; pass MaxFeeBps to accept any legal fee. Sellers may be EOAs or realms.
Description returns a listing's raw description text.
FeeBps returns the fee that will be snapshotted into newly created listings (existing listings keep their own snapshot).
FeesAccrued returns the fee pot (the F term).
Held returns the ugnot actually held at the realm address (H).
Liabilities returns everything this realm owes: UsersTotal + FeesAccrued (listings hold no value by construction).
1func ListingInfo(id int64) (seller address, title string, price, feeBps int64, status string, buyer address)ListingInfo returns a listing's fields by value: seller, title, price, snapshotted fee bps, status, and buyer (zero unless sold).
NumListings returns how many listings have ever been created.
Quote returns what a buyer pays and what the seller would receive for a listing, at its SNAPSHOTTED fee — the same arithmetic Buy performs (pattern from nsmarket/v4: a fee discovered after signing is a fee the seller was not told about).
Render shows the market at "" and a listing detail at "<id>". Titles are charset-restricted; descriptions pass through the ecosystem sanitizer.
SetFeeBps sets the protocol fee snapshotted into FUTURE listings. Existing listings keep the fee they were created under. Admin only; bounded by [0, MaxFeeBps].
SetFeeRecipient re-points the fee/surplus role, including the pot accrued so far. Admin only; zero address rejected.
Surplus returns Held() - Liabilities() (the S term).
SweepDenom sends the surplus of a single denomination to the fee recipient. For ugnot only the excess over Liabilities() moves; other denoms move wholly. Only the fee recipient may call it.
TransferAdmin STAGES a successor admin; the handoff completes only when that address calls AcceptAdmin (2-step, so a typo cannot brick administration — pattern adopted from memba_appstore_v2). Admin only; zero address rejected. Re-staging overwrites a previous stage.
UsersTotal returns the sum of all claimable proceeds (the U term).
WithdrawFees sends the accrued fee pot to the fee recipient. Only the fee recipient may call it.