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/v2 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.

Function

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/v2" — 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/v2" -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/v2" -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