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.

duel/v4 package

Overview

Package duel implements the first slice of Splash Play I, the Duel, settled from r/splash/beacon instead of a two-party commit-reveal (SPEC-09-duel-beacon.md v1.0, ../../../../SPEC-09-duel-beacon.md).

TEMPORARY NAME. "duel" is scratch space, built beside the LIVE r/splash/arcade/duel so that realm keeps working while this one is proven. The two are swapped in one commit once this one is ready, and the deploy path is decided then (SPEC-09 is silent on it on purpose). Nothing here should be linked to, or depended on, from outside this directory.

THIS SLICE IS Post, Cancel, Accept AND THE ROUND PIN. No Resolve — that is slice 2 (SPEC-09 §3 describes the whole lifecycle; the part after Accept is not built yet). A duel created here therefore has no payout path: it exists so the pin itself, SPEC-09's "only subtle part" (§3.1), can be built and proved on its own before anything is wired to money leaving the realm.

WHY COMMIT-REVEAL IS GONE RATHER THAN CARRIED OVER (SPEC-09 §1). The old duel moved OFF the beacon on 2026-09-30 because Accept needed an UPPER bound on drand's head ("round R is not signed yet"), and no arithmetic over a RELAYED round could ever establish one: a signature proves a round EXISTS (a lower bound), and the absence of one is not provable on chain, because withholding a relay is free and invisible. beacon.CurrentRound(), added 2026-10-08, derives drand's round from the BLOCK'S OWN CLOCK instead — no relayer in the path — so that upper bound now exists, and the whole reason commit-reveal's complexity existed is gone with it. What carries over is not the lifecycle but four audit findings about THIS REALM'S OWN escrow and money handling that were never specific to commit-reveal (SPEC-09 §8): rejectAttachedCoin and requireUserCall below, no transfer destination ever being a caller-supplied argument, and (for later slices) one clock/one derived value and no sentinel field standing in for a height.

THE STAKE IS WRAPPED GNOT, exactly as in r/splash/arcade/duel: this realm escrows gno.land/r/gnoland/wugnot and touches no other token.

NO Resolved, NO Winner, NO Secret, NO Nonce, NO NonceRevealedAt, NO TakerCommit, AND NO DEADLINE OF ANY KIND IN THIS SLICE'S STATE (SPEC-09 §4, §8). Resolve (slice 2) pays and deletes a duel in one call, so a resolved duel will never exist to be recorded; there is nothing here for a sentinel "never happened yet" value to be confused with a real height.

Functions

Accept

func Accept(cur realm, offerID int64) int64
Ask AI Claude ChatGPT

Accept takes a standing offer: pulls a stake equal to THE OFFER'S rung — the caller chooses nothing about the money — deletes the offer, pins the settlement round and creates the duel. Returns the new duel's ID.

THE TAKER CHOOSES THE MOMENT OF Accept AND THEREFORE THE BUCKET, AND THAT IS SAFE — STATED RATHER THAN ASSUMED (SPEC-09 §3.1, invariant 3): every bucket reachable from "now" lies in the future, so no choice among them reveals anything about the randomness it will eventually be settled with. The poster has no influence over the round at all: it is pinned HERE, at Accept, and the poster's own call happened earlier, before this duel or its bucket existed to choose.

Param

Command

# WARNING: This command is running in an INSECURE mode.
# It is strongly recommended to use a hardware device for signing
# and avoid trusting any computer connected to the internet,
# as your private keys could be exposed.

gnokey maketx call -pkgpath "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4" -func "Accept" -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -chainid "onyx-1" -remote "https://rpc.onyx.testnets.gno.land" ADDRESSgnokey query -remote "https://rpc.onyx.testnets.gno.land" auth/accounts/ADDRESS
gnokey maketx call -pkgpath "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4" -func "Accept" -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -broadcast=false ADDRESS > call.tx
gnokey sign -tx-path call.tx -chainid "onyx-1" -account-number ACCOUNTNUMBER -account-sequence SEQUENCENUMBER ADDRESS
gnokey broadcast -remote "https://rpc.onyx.testnets.gno.land" call.tx
  

BucketRounds

func BucketRounds() int64
Ask AI Claude ChatGPT

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.BucketRounds()"

Result

Cancel

func Cancel(cur realm, offerID int64)
Ask AI Claude ChatGPT

Cancel returns an unaccepted offer's stake to its poster, exactly and with no fee, and deletes the record. Poster only.

An accepted offer no longer exists — Accept deletes it in the same transaction that creates the duel — so "Cancel of an accepted offer" and "Cancel of an unknown offer" are the same refusal: whichever of Cancel or Accept lands first in block order removes the offer, and the other finds nothing. THE DESTINATION IS READ FROM THE RECORD, never a caller-supplied argument (SPEC-09 §8): a canceller can only ever recover the poster's own stake to the poster's own address.

Param

Command

# WARNING: This command is running in an INSECURE mode.
# It is strongly recommended to use a hardware device for signing
# and avoid trusting any computer connected to the internet,
# as your private keys could be exposed.

gnokey maketx call -pkgpath "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4" -func "Cancel" -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -chainid "onyx-1" -remote "https://rpc.onyx.testnets.gno.land" ADDRESSgnokey query -remote "https://rpc.onyx.testnets.gno.land" auth/accounts/ADDRESS
gnokey maketx call -pkgpath "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4" -func "Cancel" -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -broadcast=false ADDRESS > call.tx
gnokey sign -tx-path call.tx -chainid "onyx-1" -account-number ACCOUNTNUMBER -account-sequence SEQUENCENUMBER ADDRESS
gnokey broadcast -remote "https://rpc.onyx.testnets.gno.land" call.tx
  

DuelCount

func DuelCount() int64
Ask AI Claude ChatGPT

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.DuelCount()"

Result

DuelIDs

func DuelIDs(offset, limit int64) []int64
Ask AI Claude ChatGPT

Params

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.DuelIDs(,)"

Result

DuelLabel

func DuelLabel(id int64) string
Ask AI Claude ChatGPT

Param

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.DuelLabel()"

Result

DuelOf

func DuelOf(id int64) (address, address, int64, int64, int64)
Ask AI Claude ChatGPT

DuelOf reports a live duel: poster, taker, rung, THE PINNED ROUND, and the height it was accepted at.

THE ROUND IS THE IMPORTANT ONE AND IS WHY THIS SHAPE DIFFERS FROM THE SUPERSEDED REALM'S. That realm returned a reveal deadline, a resolved flag and a winner, because its duels sat in state until somebody opened them. Here Resolve pays and DELETES in the same call, so a duel in this tree is by definition unsettled and there is no winner to report. What a caller wants instead is the round -- pair it with beacon.TimeOfRound(round) and you have the countdown.

Param

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.DuelOf()"

Result

DuelRound

func DuelRound(id int64) int64
Ask AI Claude ChatGPT

DuelRound is DuelOf's fourth value on its own, because it is the one a countdown polls and a caller should not have to decode a five-tuple to get it. 0 for an unknown or already-settled duel.

Param

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.DuelRound()"

Result

Escrowed

func Escrowed() int64
Ask AI Claude ChatGPT

Escrowed is the players' money this realm is holding: every standing offer's stake plus both stakes of every live duel. It does NOT include pendingPot, which is the raffle's share already earned and awaiting delivery -- read PendingPot for that. Two separate numbers because they are two separate claims, and summing them would hide which is which.

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.Escrowed()"

Result

FlushPot

func FlushPot(cur realm) int64
Ask AI Claude ChatGPT

FlushPot delivers the whole booked rake — pendingPot and the entries earned alongside it — to r/splash/arcade/raffle's CURRENT week, through raffle.FundPotWithEntries (or raffle.FundPot when nobody earned an entry), and returns the amount actually delivered. PERMISSIONLESS: anyone may pay the gas to do it, and the destination is not theirs to choose. Ported from r/splash/arcade/duel/duel.gno's FlushPot, same shape and same reasoning: a resolved duel's payout must never depend on another realm's bookkeeping (see pendingPot's own doc comment above), so delivery is a SEPARATE, later, permissionless call whose only failure mode costs the CALLER OF THIS FUNCTION a reverted transaction — never a duel's winner, who has already been paid and whose record is already gone.

A NO-OP, NOT A PANIC, WHEN NOTHING IS BOOKED — the FIRST of two deliberate departures from the old realm's identical function, which panics in this case. A permissionless call anybody (in practice: a keeper, on a schedule) may make at any time is better served by a harmless zero than by forcing every caller to read PendingPot first just to avoid an abort.

THE SECOND DEPARTURE: pendingPot AND THE REST OF THE BOOKING ARE ZEROED ONLY AFTER raffle.FundPot/FundPotWithEntries HAS RETURNED WITHOUT PANICKING — not before, the old realm's own ordering. ON A REAL CHAIN THIS ORDERING CHOICE DOES NOT MATTER: a panic reverts the WHOLE transaction, so whether the zeroing happened before or after the cross-realm call, it is discarded along with everything else the call did, and the old realm's "before" ordering is exactly as revert-safe as this file's "after" ordering. RE-ENTRANCY THROUGH THE CROSS-REALM CALL ITSELF IS SEPARATELY UNREACHABLE, ARCHITECTURALLY, NOT BY CONVENTION: this file imports raffle, so raffle cannot import this file back without a compile-time import cycle, and nothing else in raffle's own code calls into this realm — there is no path by which raffle.FundPot/ FundPotWithEntries could call back into FlushPot mid-execution regardless of ordering. A mutation check (reorder this file to zero first, the old realm's way) DOES make TestFlushPotRefusedDeliveryLeavesBookingIntactAndRetryable fail — but that result is a property of gno test's HARNESS, not of the chain: uassert.AbortsContains recovers the panic inside the same test process without re-running the whole message atomically, so a package-level write made before the panic (the zeroing) survives the recover when a real chain's revert would have discarded it. The harness's recover is not a state rollback, and the old realm's comment overstated what the mutation test proves by citing it as if it were one. The ordering below is kept anyway — zeroing after costs nothing and makes the retry property visible in THIS function's own control flow rather than resting on a chain-level guarantee this suite cannot exercise — but the reason to keep it is suite-observability, not a chain safety property the old ordering lacked.

THE RULE THIS FILE ACTUALLY FOLLOWS, SO THE TWO ORDERINGS IN THIS SECTION DO NOT READ AS A CONTRADICTION: every OTHER state change in this file (Cancel's escrowed -=, Accept's offers.Remove, Resolve's duels.Remove) mutates bookkeeping BEFORE the paying wugnot.Transfer, on the general principle that a retry or a re-entrant call should find nothing rather than something stale. SweepStuckRake, below, follows that same general rule: its wugnot.Transfer is a plain, ungated call that this function's own prior checks (and checkInvariant's balance assertion) already make as close to unconditional as this realm can establish, so there is nothing to protect by delaying the zeroing. FlushPot is the ONE deliberate exception to the general rule, and the reason is specific to it: its cross-realm call is, TODAY, KNOWN to panic unconditionally (the raffle's package-path gate, documented below), so zeroing after is what keeps the retry property visible inside this function's own control flow for the one call in this file whose failure is not hypothetical.

END-TO-END DELIVERY IS UNPROVEN BY THIS SUITE, AND THAT IS STATED HERE RATHER THAN DISCOVERED LATER (SPEC-09 §8a — found during slice 2 and a certainty, not a risk). raffle.gno's FundPot and FundPotWithEntries both gate on cur.Previous().PkgPath() being EXACTLY "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v3" — the OLD realm's own path, never this realm's ".../arcade/duel/v2" — so EVERY CALL THIS FUNCTION MAKES TO THE RAFFLE TODAY PANICS, UNCONDITIONALLY, and that panic reverts this whole call with pendingPot intact (the retry property above, independently of the zero-after ordering, since nothing here has executed the zeroing yet when the panic happens). This is §8a's forced deploy order working as designed, not a defect to chase here: the raffle must be redeployed FIRST, naming this realm's path, before this function's happy path can be exercised for real — and it will be, for the first time, at that deploy, not in this suite. What this suite proves instead, and all it honestly can: the booking is correct, a refused delivery loses nothing and remains retryable, the books balance, this function is permissionless, and it refuses attached coin.

Command

# WARNING: This command is running in an INSECURE mode.
# It is strongly recommended to use a hardware device for signing
# and avoid trusting any computer connected to the internet,
# as your private keys could be exposed.

gnokey maketx call -pkgpath "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4" -func "FlushPot" -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -chainid "onyx-1" -remote "https://rpc.onyx.testnets.gno.land" ADDRESSgnokey query -remote "https://rpc.onyx.testnets.gno.land" auth/accounts/ADDRESS
gnokey maketx call -pkgpath "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4" -func "FlushPot" -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -broadcast=false ADDRESS > call.tx
gnokey sign -tx-path call.tx -chainid "onyx-1" -account-number ACCOUNTNUMBER -account-sequence SEQUENCENUMBER ADDRESS
gnokey broadcast -remote "https://rpc.onyx.testnets.gno.land" call.tx
  

OfferCount

func OfferCount() int64
Ask AI Claude ChatGPT

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.OfferCount()"

Result

OfferIDs

func OfferIDs(offset, limit int64) []int64
Ask AI Claude ChatGPT

OfferIDs and DuelIDs page through the boards in key order. offset and limit are CLAMPED rather than validated: a caller asking for a million gets pageLimit, and one asking past the end gets an empty slice. A reader that panics on a bad page is a reader a front end has to guard against.

Params

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.OfferIDs(,)"

Result

OfferOf

func OfferOf(id int64) (address, int64, int64)
Ask AI Claude ChatGPT

OfferOf reports a standing offer: poster, rung, posted-at height. All zero for an id that is not on the table -- the same "zero means absent" shape the superseded realm used, so a front end can tell absent from present without a second call.

Param

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.OfferOf()"

Result

PendingEntryHolders

func PendingEntryHolders() int64
Ask AI Claude ChatGPT

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.PendingEntryHolders()"

Result

PendingEntryUnits

func PendingEntryUnits() int64
Ask AI Claude ChatGPT

PendingEntryUnits and PendingEntryHolders are what FlushPot will deliver alongside the pot. A keeper reads all three to decide whether calling is worth the gas.

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.PendingEntryUnits()"

Result

PendingPot

func PendingPot() int64
Ask AI Claude ChatGPT

PendingPot is the raffle's share of settled duels' rake, earned and booked but not yet delivered. FlushPot's own doc comment told callers to read this and IT DID NOT EXIST -- a comment pointing at a function nobody wrote, which is the shape this project has met repeatedly today.

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.PendingPot()"

Result

PendingSince

func PendingSince() int64
Ask AI Claude ChatGPT

PendingSince is the height the current booking started accruing, or 0 when nothing is booked. It is SweepStuckRake's grace clock, exposed so the wait is visible rather than inferred.

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.PendingSince()"

Result

PinOffsetRounds

func PinOffsetRounds() int64
Ask AI Claude ChatGPT

PinOffsetRounds and BucketRounds expose the pin arithmetic, so a front end can work out WHICH FLIP a new acceptance would land in -- "4 minutes left to get into this one" -- without keeping a second copy of either number.

THAT SECOND COPY IS THE WHOLE REASON THESE EXIST. The constants are a security budget (read pinOffsetRounds' comment), and a JavaScript duplicate of a security budget is one that drifts silently the moment anyone retunes it. The front end asks the realm instead. Same reasoning and same shape as arcade/speed's PinOffsetRounds.

They are reads of compile-time constants: no state, no caller, nothing to guard.

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.PinOffsetRounds()"

Result

Post

func Post(cur realm, rung int64, opponent address, label string) int64
Ask AI Claude ChatGPT

Post pulls rungs[rung] from the caller through the wugnot allowance and leaves it standing in the lobby. Returns the new offer's ID.

opponent, when non-zero, addresses the offer to one player: only that address may later Accept it. Zero ("") leaves it open to anyone.

label is free text for a future render, at most maxLabelLen bytes ("" is no label). Neither argument touches settlement: the pinned round and the beacon's randomness decide that, not this call.

Params

Command

# WARNING: This command is running in an INSECURE mode.
# It is strongly recommended to use a hardware device for signing
# and avoid trusting any computer connected to the internet,
# as your private keys could be exposed.

gnokey maketx call -pkgpath "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4" -func "Post" -args $'' -args $'' -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -chainid "onyx-1" -remote "https://rpc.onyx.testnets.gno.land" ADDRESSgnokey query -remote "https://rpc.onyx.testnets.gno.land" auth/accounts/ADDRESS
gnokey maketx call -pkgpath "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4" -func "Post" -args $'' -args $'' -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -broadcast=false ADDRESS > call.tx
gnokey sign -tx-path call.tx -chainid "onyx-1" -account-number ACCOUNTNUMBER -account-sequence SEQUENCENUMBER ADDRESS
gnokey broadcast -remote "https://rpc.onyx.testnets.gno.land" call.tx
  

Render

func Render(path string) string
Ask AI Claude ChatGPT

Param

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.Render()"

Result

Resolve

func Resolve(cur realm, duelID int64)
Ask AI Claude ChatGPT

Resolve computes a duel's winner from the published beacon round, pays and deletes the record — ALL IN ONE CALL. PERMISSIONLESS: there is no Collect in this design (SPEC-09 §3), so a winner never has to come back, and anybody — a party, a bystander, a keeper — may call this once the pinned round is on chain. The caller supplies nothing but duelID: the winner, the payout and the entries are all non-discretionary functions of stored state and the beacon's own randomness (invariant 4).

ORDER OF OPERATIONS, SPEC-09'S OWN: reject attached coin first, as every entrypoint; look up the duel, refusing an unknown one; refuse if the pinned round is not yet published; THEN compute the outcome, pay, book entries, delete, emit.

THE PAYOUT IS PORTED FROM r/splash/arcade/duel/duel.gno's settle, NOT REDESIGNED (SPEC-09 §8, this task's own instruction): same pool/rake arithmetic, same 60/40 split with ops floored and dust to the pot, same splitEntries/rakeEntryUnits weighting, same conservation assertion. THE ONE DEPARTURE, AND IT IS DELIBERATE RATHER THAN AN OMISSION, IS WHERE THE POT SHARE AND ITS ENTRIES LAND. The old settle's own comment says it plainly — "BOOKED, NOT SENT, AND NOT ONE LINE OF RAFFLE CODE RUNS IN THIS TRANSACTION" — and books into pendingPot/pendingEntries for a SEPARATE, later FlushPot to deliver, specifically so a raffle-side refusal can never take a winner's payout down with it. This file ports exactly that half of settle (see pendingPot's own doc comment above) rather than a direct, same-transaction call to raffle.FundPotWithEntries, because such a call would not merely risk the winner's payout here — IT WOULD PANIC EVERY SINGLE TIME, UNCONDITIONALLY: raffle.gno's FundPot and FundPotWithEntries both gate on cur.Previous().PkgPath() == the literal string "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v3" — the OLD realm's own package path — and this realm's module is ".../arcade/duel/v2" (SPEC-09 §8b, set in slice 3's gnomod.toml), a different string, so that check fails on every call for as long as the raffle's compile-time constant names the old path. A direct call here would make Resolve permanently unable to pay any duel whose rake is positive (every rung's is), which is every duel — the opposite of SPEC-09 invariant 7 ("a duel can never be stuck by inaction"). ACTUALLY DELIVERING THE BOOKED POT IS FlushPot, BELOW (THIS SLICE, SPEC-09 §8a.4) — permissionless, separate from this call, and provably safe for a winner's payout even while the raffle refuses it. Nothing is lost in the meantime: the booked wugnot sits in this realm's own balance, accounted for by pendingPot in checkInvariant, exactly as it would between settle and FlushPot in the old realm today.

Param

Command

# WARNING: This command is running in an INSECURE mode.
# It is strongly recommended to use a hardware device for signing
# and avoid trusting any computer connected to the internet,
# as your private keys could be exposed.

gnokey maketx call -pkgpath "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4" -func "Resolve" -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -chainid "onyx-1" -remote "https://rpc.onyx.testnets.gno.land" ADDRESSgnokey query -remote "https://rpc.onyx.testnets.gno.land" auth/accounts/ADDRESS
gnokey maketx call -pkgpath "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4" -func "Resolve" -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -broadcast=false ADDRESS > call.tx
gnokey sign -tx-path call.tx -chainid "onyx-1" -account-number ACCOUNTNUMBER -account-sequence SEQUENCENUMBER ADDRESS
gnokey broadcast -remote "https://rpc.onyx.testnets.gno.land" call.tx
  

ResultCount

func ResultCount() int64
Ask AI Claude ChatGPT

ResultCount is how many settlements the ring currently holds, which is at most resultRingSize. Read ResultsSeen for how many there have ever been.

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.ResultCount()"

Result

ResultRingSize

func ResultRingSize() int64
Ask AI Claude ChatGPT

ResultRingSize is the bound, exported so a front end can say "last 32" without hardcoding a number that might change.

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.ResultRingSize()"

Result

ResultsSeen

func ResultsSeen() int64
Ask AI Claude ChatGPT

ResultsSeen is every settlement this realm has ever made, including those the ring has overwritten. The gap between this and ResultCount is exactly what a caller must go to the event log for.

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.ResultsSeen()"

Result

RoundForAcceptAt

func RoundForAcceptAt(currentRound int64) int64
Ask AI Claude ChatGPT

RoundForAcceptAt is THE question a front end actually has, answered by the realm that owns the answer: if a duel were accepted while the beacon reads `currentRound`, which round would it be pinned to?

It is the exact expression Accept uses, factored out so there is ONE copy of it and both callers share it -- see pinnedRoundFor below. A front end can therefore show a truthful join deadline: the answer changes at a bucket boundary, and the moment it changes is the moment the current flip closes to newcomers.

Param

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.RoundForAcceptAt()"

Result

RungCount

func RungCount() int64
Ask AI Claude ChatGPT

RungCount and StakeOfRung expose the ladder so a front end need not hardcode it. Same names and shapes as the superseded realm, deliberately, so the existing front end's reads.js keeps working.

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.RungCount()"

Result

StakeOfRung

func StakeOfRung(rung int64) int64
Ask AI Claude ChatGPT

Param

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.StakeOfRung()"

Result

SweepStuckRake

func SweepStuckRake(cur realm) int64
Ask AI Claude ChatGPT

SweepStuckRake is the LAST RESORT for a booked rake the raffle will not take, so that pendingPot is never a place money can enter and never leave. It transfers the WHOLE booked amount to the raffle's address by a PLAIN wugnot.Transfer — no raffle code runs, so nothing in the raffle can refuse it — and returns the amount moved. PERMISSIONLESS, and only once the batch's YOUNGEST CLAIM (pendingSince) has stood for pendingGraceBlocks. Ported from r/splash/arcade/duel/duel.gno's SweepStuckRake — same mechanism, same grace window, same dropped-entries cost, same reasoning; read that function's own doc comment for the full argument, including the gas figures for a full 128-holder batch. This comment restates only what is specific to deciding to port it here at all.

WHY PORT A GRACE-WINDOW LAST RESORT WHEN §8a's REFUSAL IS A CERTAINTY, NOT THE OCCASIONAL TROUBLE THE OLD REALM'S VERSION IS A LAST RESORT FOR — this is the one judgment call this slice makes and states plainly rather than assuming. The invariant SweepStuckRake protects ("a place money can enter and never leave") does not care WHY FlushPot's door is shut, only THAT it might stay shut indefinitely. Today it is shut by construction — the raffle's duelRealmPkgPath names the old realm, not this one — and it reopens only when the raffle is redeployed naming this realm's path (SPEC-09 §8b). Resolve keeps booking pendingPot regardless of whether that redeploy ever happens, is delayed, or the swap this directory is scaffolding for is abandoned; Resolve must not, and does not, know or care. Without this function that booking would have exactly one exit (FlushPot), and that exit is the one this entire slice's own doc comment says is unproven. A grace-windowed, gate-bypassing last resort is cheap insurance against that exit never opening, and the mechanism — a plain transfer that runs no raffle code — does not depend on knowing why the gate is shut, only on giving the booking a second way out that the gate cannot touch.

THE BOOKED ENTRIES ARE DROPPED ON THIS PATH, exactly as in the old realm's SweepStuckRake and for the identical reason: the money lands in the raffle's ops pot via sweepDonations, not a prize pot, so there is no draw left for an entry to be an entry IN — and an entry with no rake behind it is exactly what checkInvariant refuses. The 60/40 split is NOT preserved on this path (it all becomes the house's ops pot); that is the honest cost of a window existing at all, and it is preventable by anyone, at any time, by simply calling FlushPot instead.

Panics if there is nothing booked, or if the window has not elapsed.

Command

# WARNING: This command is running in an INSECURE mode.
# It is strongly recommended to use a hardware device for signing
# and avoid trusting any computer connected to the internet,
# as your private keys could be exposed.

gnokey maketx call -pkgpath "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4" -func "SweepStuckRake" -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -chainid "onyx-1" -remote "https://rpc.onyx.testnets.gno.land" ADDRESSgnokey query -remote "https://rpc.onyx.testnets.gno.land" auth/accounts/ADDRESS
gnokey maketx call -pkgpath "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4" -func "SweepStuckRake" -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -broadcast=false ADDRESS > call.tx
gnokey sign -tx-path call.tx -chainid "onyx-1" -account-number ACCOUNTNUMBER -account-sequence SEQUENCENUMBER ADDRESS
gnokey broadcast -remote "https://rpc.onyx.testnets.gno.land" call.tx
  

RecentResults

func RecentResults(limit int64) []Result
Ask AI Claude ChatGPT

RecentResults returns up to `limit` settlements, MOST RECENT FIRST. limit is clamped to the ring's size.

Param

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/arcade/duel/v4.RecentResults()"

Result